package org.seckill.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.seckill.dao.SecKillDao;
import org.seckill.dao.SuccessKilledDao;
import org.seckill.dao.cache.RedisDao;
import org.seckill.dto.Exposer;
import org.seckill.dto.SeckillExecution;
import org.seckill.entity.Seckill;
import org.seckill.entity.SuccessKilled;
import org.seckill.enums.SeckillStateEnum;
import org.seckill.exception.RepeatKillException;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;
import org.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

/**
 * 秒杀服务实现
 * Created by yuqin on 2016/6/11.
 */
@Service
public class SeckillServiceImpl implements SeckillService {
    private static final String slat = "asdf65891234578)&*(^&*w345asd~~";

    private Logger logger = LoggerFactory.getLogger(SeckillServiceImpl.class);

    @Autowired
    private SecKillDao secKillDao;

    @Autowired
    private SuccessKilledDao successKilledDao;

    @Autowired
    private RedisDao redisDao; 

    @Override
    public List<Seckill> getSeckillList() {
        return secKillDao.queryAll(0, 5);
    }

    @Override
    public Seckill getById(long seckillId) {
        return secKillDao.queryById(seckillId);
    }

    @Override
    public Exposer exportSeckillUrl(long seckillId) {
		// 优化点
		// 1.访问redis
		Seckill seckill = redisDao.getSeckill(seckillId);
		if (seckill == null) {
			// 2.访问数据库
			seckill = secKillDao.queryById(seckillId);
			if (seckill == null) {
				return new Exposer(seckillId, false);
			} else {
				// 3.放入到缓存
				redisDao.putSeckill(seckill);
			}
		}
		//优化之前
//        Seckill seckill = secKillDao.queryById(seckillId);
//        if (seckill == null) {
//            return new Exposer(seckillId, false);
//        }
        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();
        //系统当前时间
        Date nowTime = new Date();
        if (nowTime.before(nowTime) || nowTime.after(endTime)) {
            return new Exposer(seckillId, false, nowTime.getTime(), startTime.getTime(), endTime.getTime());
        }
        //获取秒杀的md5
        String md5 = getMD5(seckillId);
        return new Exposer(seckillId, true, md5);
    }

	/**
	 * 优化流程<br>
	 * 1.插入秒杀详情(插入操作没有行级锁)
	 * 2.间库存(有锁)
	 * 3.commit/rollback
	 */
    @Transactional
    @Override
    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException {
        if (md5 == null || !getMD5(seckillId).equals(md5)) {
            throw new SeckillException("seckill data rewrite");
        }
        //执行秒杀逻辑：减库存 + 记录购买行为
        Date nowTime = new Date();
        try {
            //1.记录购买行为
            int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
            //唯一
            if (insertCount <= 0) {
                //重复秒杀
                throw new RepeatKillException("seckill repeated");
            } else {
            	//2.减库存（数据库行级锁）
            	int updateCount = secKillDao.reduceNumber(seckillId, nowTime);
            	if (updateCount <= 0) {
                    //没有更新记录秒结束
                    throw new SeckillCloseException("seckill is closed");
                }
                //秒杀成功
                SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
                return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled);
            }
        } catch (SeckillCloseException e1) {
            throw e1;
        } catch (RepeatKillException e2) {
            throw e2;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new SeckillException("seckill inner error:" + e.getMessage());
        }
    }

    /**
     * 获取秒杀的md5
     *
     * @param seckillId 秒杀的id
     * @return 秒杀的md5
     */
    private String getMD5(long seckillId) {
        String hash = seckillId + slat;
        return DigestUtils.md5DigestAsHex(hash.getBytes());
    }

	@Override
	public SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5)
			throws SeckillException, RepeatKillException, SeckillCloseException {
        if (md5 == null || !getMD5(seckillId).equals(md5)) {
            throw new SeckillException("seckill data rewrite");
        }
        //执行秒杀逻辑：减库存 + 记录购买行为
        Date nowTime = new Date();
        Map<String, Object> paramMap = new HashMap<String,Object>();
        paramMap.put("seckillId", seckillId);
        paramMap.put("phone", userPhone);
        paramMap.put("killTime", nowTime);
        paramMap.put("result", null);
        //执行存储过程，result被设置
        try {
        	secKillDao.killByProcedure(paramMap);
        	//获取result
        	int result = MapUtils.getInteger(paramMap, "result",-2);
        	//
        	if(result == 1){//秒杀成功
        		SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
        		return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled);
        	}else{
        		return new SeckillExecution(seckillId,SeckillStateEnum.stateOf(result));
        	}
		} catch (Exception e) {
			return new SeckillExecution(seckillId,SeckillStateEnum.INNER_ERROR);
		}
	}
}
